<?php

namespace Flames\Dump\Parsers;

use Flames\Dump\Inc\DumpHelper;
use Flames\Dump\Parsers\DumpParserInterface;

/**
 * @internal
 */
class DumpParsersColor implements DumpParserInterface
{
    private static $colorNames = array(
        'aliceblue' => '#f0f8ff',
        'antiquewhite' => '#faebd7',
        'aqua' => '#00ffff',
        'aquamarine' => '#7fffd4',
        'azure' => '#f0ffff',
        'beige' => '#f5f5dc',
        'bisque' => '#ffe4c4',
        'black' => '#000000',
        'blanchedalmond' => '#ffebcd',
        'blue' => '#0000ff',
        'blueviolet' => '#8a2be2',
        'brown' => '#a52a2a',
        'burlywood' => '#deb887',
        'cadetblue' => '#5f9ea0',
        'chartreuse' => '#7fff00',
        'chocolate' => '#d2691e',
        'coral' => '#ff7f50',
        'cornflowerblue' => '#6495ed',
        'cornsilk' => '#fff8dc',
        'crimson' => '#dc143c',
        'cyan' => '#00ffff',
        'darkblue' => '#00008b',
        'darkcyan' => '#008b8b',
        'darkgoldenrod' => '#b8860b',
        'darkgray' => '#a9a9a9',
        'darkgrey' => '#a9a9a9',
        'darkgreen' => '#006400',
        'darkkhaki' => '#bdb76b',
        'darkmagenta' => '#8b008b',
        'darkolivegreen' => '#556b2f',
        'darkorange' => '#ff8c00',
        'darkorchid' => '#9932cc',
        'darkred' => '#8b0000',
        'darksalmon' => '#e9967a',
        'darkseagreen' => '#8fbc8f',
        'darkslateblue' => '#483d8b',
        'darkslategray' => '#2f4f4f',
        'darkslategrey' => '#2f4f4f',
        'darkturquoise' => '#00ced1',
        'darkviolet' => '#9400d3',
        'deeppink' => '#ff1493',
        'deepskyblue' => '#00bfff',
        'dimgray' => '#696969',
        'dimgrey' => '#696969',
        'dodgerblue' => '#1e90ff',
        'firebrick' => '#b22222',
        'floralwhite' => '#fffaf0',
        'forestgreen' => '#228b22',
        'fuchsia' => '#ff00ff',
        'gainsboro' => '#dcdcdc',
        'ghostwhite' => '#f8f8ff',
        'gold' => '#ffd700',
        'goldenrod' => '#daa520',
        'gray' => '#808080',
        'grey' => '#808080',
        'green' => '#008000',
        'greenyellow' => '#adff2f',
        'honeydew' => '#f0fff0',
        'hotpink' => '#ff69b4',
        'indianred' => '#cd5c5c',
        'indigo' => '#4b0082',
        'ivory' => '#fffff0',
        'khaki' => '#f0e68c',
        'lavender' => '#e6e6fa',
        'lavenderblush' => '#fff0f5',
        'lawngreen' => '#7cfc00',
        'lemonchiffon' => '#fffacd',
        'lightblue' => '#add8e6',
        'lightcoral' => '#f08080',
        'lightcyan' => '#e0ffff',
        'lightgoldenrodyellow' => '#fafad2',
        'lightgray' => '#d3d3d3',
        'lightgrey' => '#d3d3d3',
        'lightgreen' => '#90ee90',
        'lightpink' => '#ffb6c1',
        'lightsalmon' => '#ffa07a',
        'lightseagreen' => '#20b2aa',
        'lightskyblue' => '#87cefa',
        'lightslategray' => '#778899',
        'lightslategrey' => '#778899',
        'lightsteelblue' => '#b0c4de',
        'lightyellow' => '#ffffe0',
        'lime' => '#00ff00',
        'limegreen' => '#32cd32',
        'linen' => '#faf0e6',
        'magenta' => '#ff00ff',
        'maroon' => '#800000',
        'mediumaquamarine' => '#66cdaa',
        'mediumblue' => '#0000cd',
        'mediumorchid' => '#ba55d3',
        'mediumpurple' => '#9370d8',
        'mediumseagreen' => '#3cb371',
        'mediumslateblue' => '#7b68ee',
        'mediumspringgreen' => '#00fa9a',
        'mediumturquoise' => '#48d1cc',
        'mediumvioletred' => '#c71585',
        'midnightblue' => '#191970',
        'mintcream' => '#f5fffa',
        'mistyrose' => '#ffe4e1',
        'moccasin' => '#ffe4b5',
        'navajowhite' => '#ffdead',
        'navy' => '#000080',
        'oldlace' => '#fdf5e6',
        'olive' => '#808000',
        'olivedrab' => '#6b8e23',
        'orange' => '#ffa500',
        'orangered' => '#ff4500',
        'orchid' => '#da70d6',
        'palegoldenrod' => '#eee8aa',
        'palegreen' => '#98fb98',
        'paleturquoise' => '#afeeee',
        'palevioletred' => '#d87093',
        'papayawhip' => '#ffefd5',
        'peachpuff' => '#ffdab9',
        'peru' => '#cd853f',
        'pink' => '#ffc0cb',
        'plum' => '#dda0dd',
        'powderblue' => '#b0e0e6',
        'purple' => '#800080',
        'red' => '#ff0000',
        'rosybrown' => '#bc8f8f',
        'royalblue' => '#4169e1',
        'saddlebrown' => '#8b4513',
        'salmon' => '#fa8072',
        'sandybrown' => '#f4a460',
        'seagreen' => '#2e8b57',
        'seashell' => '#fff5ee',
        'sienna' => '#a0522d',
        'silver' => '#c0c0c0',
        'skyblue' => '#87ceeb',
        'slateblue' => '#6a5acd',
        'slategray' => '#708090',
        'slategrey' => '#708090',
        'snow' => '#fffafa',
        'springgreen' => '#00ff7f',
        'steelblue' => '#4682b4',
        'tan' => '#d2b48c',
        'teal' => '#008080',
        'thistle' => '#d8bfd8',
        'tomato' => '#ff6347',
        'turquoise' => '#40e0d0',
        'violet' => '#ee82ee',
        'wheat' => '#f5deb3',
        'white' => '#ffffff',
        'whitesmoke' => '#f5f5f5',
        'yellow' => '#ffff00',
        'yellowgreen' => '#9acd32'
    );

    public function replacesAllOtherParsers()
    {
        return false;
    }

    public function parse(&$variable, $varData)
    {
        if (!$this->_fits($variable)) {
            return false;
        }

        // todo after we migrate from less:
        // $originalVarData->name .= "<div style=\"background:{$variable}\" class=\"_sage-color-preview\">{$variable}</div>";

        $variants = $this->_convert($variable);
        $value = <<<HTML
<div style="background:{$variable}" class="_sage-color-preview">{$variable}</div><strong>hex:</strong> {$variants['hex']}
<strong>rgb:</strong> {$variants['rgb']}
<strong>hsl:</strong> {$variants['hsl']}
HTML;
        if (array_key_exists('name', $variants)) {
            $value .= PHP_EOL . "<strong>css:</strong> {$variants['name']}";
        }

        $varData->addTabToView($variable, 'CSS color', $value);
    }

    private function _fits($variable)
    {
        if (!DumpHelper::isRichMode()) {
            return false;
        }

        if (!is_string($variable)) {
            return false;
        }

        if (strlen($variable) > 32) {
            return false;
        }

        $var = strtolower(trim($variable));

        return isset(self::$colorNames[$var])
            || preg_match(
                '/^(?:#[0-9A-Fa-f]{3}|#[0-9A-Fa-f]{6}|(?:rgb|hsl)a?\s*\((?:\s*[0-9.%]+\s*,?){3,4}\))$/',
                $var
            );
    }

    private function _convert($color)
    {
        $color = strtolower($color);
        $decimalColors = array();
        $variants = array(
            'hex' => null,
            'rgb' => null,
            'name' => null,
            'hsl' => null,
        );

        if (isset(self::$colorNames[$color])) {
            $variants['name'] = $color;
            $color = self::$colorNames[$color];
        }

        if ($color[0] === '#') {
            $variants['hex'] = $color;
            $color = substr($color, 1);
            if (strlen($color) === 6) {
                $colors = str_split($color, 2);
            } else {
                $colors = array(
                    $color[0] . $color[0],
                    $color[1] . $color[1],
                    $color[2] . $color[2],
                );
            }

            $decimalColors = array_map('hexdec', $colors);
        } elseif (substr($color, 0, 3) === 'rgb') {
            $variants['rgb'] = $color;
            preg_match_all('#([0-9.%]+)#', $color, $matches);
            $decimalColors = $matches[1];
            foreach ($decimalColors as &$color) {
                if (strpos($color, '%') !== false) {
                    $color = str_replace('%', '', $color) * 2.55;
                }
            }
        } elseif (substr($color, 0, 3) === 'hsl') {
            $variants['hsl'] = $color;
            preg_match_all('#([0-9.%]+)#', $color, $matches);

            $colors = $matches[1];
            $colors[0] /= 360;
            $colors[1] = str_replace('%', '', $colors[1]) / 100;
            $colors[2] = str_replace('%', '', $colors[2]) / 100;

            $decimalColors = $this->_HSLtoRGB($colors);
            if (isset($colors[3])) {
                $decimalColors[] = $colors[3];
            }
        }

        if (isset($decimalColors[3])) {
            $alpha = $decimalColors[3];
            unset($decimalColors[3]);
        } else {
            $alpha = null;
        }
        foreach ($variants as $type => &$variant) {
            if (isset($variant)) {
                continue;
            }

            switch ($type) {
                case 'hex':
                    $variant = '#';
                    foreach ($decimalColors as &$color) {
                        $variant .= str_pad(dechex($color), 2, '0', STR_PAD_LEFT);
                    }
                    $variant .= isset($alpha) ? ' (alpha omitted)' : '';
                    break;
                case 'rgb':
                    $rgb = $decimalColors;
                    if (isset($alpha)) {
                        $rgb[] = $alpha;
                        $a = 'a';
                    } else {
                        $a = '';
                    }
                    $variant = "rgb{$a}( " . implode(', ', $rgb) . ' )';
                    break;
                case 'hsl':
                    $rgb = $this->_RGBtoHSL($decimalColors);
                    if ($rgb === null) {
                        unset($variants[$type]);
                        break;
                    }
                    if (isset($alpha)) {
                        $rgb[] = $alpha;
                        $a = 'a';
                    } else {
                        $a = '';
                    }

                    $variant = "hsl{$a}( " . implode(', ', $rgb) . ' )';
                    break;
                case 'name':
                    // [!] name in initial variants array must go after hex
                    if (($key = array_search($variants['hex'], self::$colorNames, true)) !== false) {
                        $variant = $key;
                    } else {
                        unset($variants[$type]);
                    }
                    break;
            }
        }

        return $variants;
    }

    private function _HSLtoRGB(array $hsl)
    {
        list($h, $s, $l) = $hsl;
        $m2 = ($l <= 0.5) ? $l * ($s + 1) : $l + $s - $l * $s;
        $m1 = $l * 2 - $m2;

        return array(
            round($this->_hue2rgb($m1, $m2, $h + 0.33333) * 255),
            round($this->_hue2rgb($m1, $m2, $h) * 255),
            round($this->_hue2rgb($m1, $m2, $h - 0.33333) * 255),
        );
    }

    /**
     * Helper function for _color_hsl2rgb().
     */
    private function _hue2rgb($m1, $m2, $h)
    {
        $h = ($h < 0) ? $h + 1 : (($h > 1) ? $h - 1 : $h);
        if ($h * 6 < 1) {
            return $m1 + ($m2 - $m1) * $h * 6;
        }
        if ($h * 2 < 1) {
            return $m2;
        }
        if ($h * 3 < 2) {
            return $m1 + ($m2 - $m1) * (0.66666 - $h) * 6;
        }

        return $m1;
    }

    private function _RGBtoHSL(array $rgb)
    {
        list($clrR, $clrG, $clrB) = $rgb;

        $clrMin = min($clrR, $clrG, $clrB);
        $clrMax = max($clrR, $clrG, $clrB);
        $deltaMax = $clrMax - $clrMin;

        $L = ($clrMax + $clrMin) / 510;

        if (0 == $deltaMax) {
            $H = 0;
            $S = 0;
        } else {
            if (0.5 > $L) {
                $S = $deltaMax / ($clrMax + $clrMin);
            } else {
                $S = $deltaMax / (510 - $clrMax - $clrMin);
            }

            if ($clrMax == $clrR) {
                $H = ($clrG - $clrB) / (6.0 * $deltaMax);
            } elseif ($clrMax == $clrG) {
                $H = 1 / 3 + ($clrB - $clrR) / (6.0 * $deltaMax);
            } else {
                $H = 2 / 3 + ($clrR - $clrG) / (6.0 * $deltaMax);
            }

            if (0 > $H) {
                $H += 1;
            }
            if (1 < $H) {
                $H -= 1;
            }
        }

        return array(
            round($H * 360),
            round($S * 100) . '%',
            round($L * 100) . '%'
        );
    }
}
